package com.skivingcloud.admin.sys.controller;

import cn.hutool.core.io.IoUtil;
import com.skivingcloud.admin.conf.PasswordExpired;
import com.skivingcloud.admin.security.SecurityUser;
import com.skivingcloud.admin.security.UserUtil;
import com.skivingcloud.admin.sys.entity.SysLoginForm;
import com.skivingcloud.admin.sys.entity.SysRole;
import com.skivingcloud.admin.sys.entity.SysUser;
import com.skivingcloud.admin.sys.service.SysCaptchaService;
import com.skivingcloud.admin.sys.service.SysDashboardSettingService;
import com.skivingcloud.admin.sys.service.SysRoleMenuService;
import com.skivingcloud.admin.sys.service.SysUserService;
import com.skivingcloud.admin.sys.vo.AdminRoute;
import com.skivingcloud.admin.sys.vo.AdminRouteVue;
import com.skivingcloud.common.utils.ReturnT;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author hushouquan
 */
@Tag(name = "SysUserAuthController", description = "认证相关API")
@RestController
@RequestMapping("/sys/auth")
public class SysUserAuthController{
    
    private static final Logger log = LoggerFactory.getLogger(SysUserAuthController.class);
    
    @Resource
    private SysCaptchaService sysCaptchaService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private PasswordExpired passwordExpired;
    @Resource
    private SysRoleMenuService sysRoleMenuService;
    @Resource
    private SysDashboardSettingService sysDashboardSettingService;
    /**
     * 获取验证码
     * @param response 返回
     * @param uuid 生成验证码的uuid
     * @throws Exception 异常
     */
    @Operation(summary = "加载验证码图片", description = "验证码图片",
            parameters = {@Parameter(name = "uuid", description = "随机uuid")})
    @ApiResponse(responseCode = "2xx",description = "验证码图片")
    @GetMapping("/captcha.jpg")
    public void captcha(HttpServletResponse response, @RequestParam(name="uuid") String uuid) throws Exception {
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/png");
        
        //获取图片验证码
        BufferedImage image = sysCaptchaService.getCaptcha(uuid);
        
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image, "png", out);
        IoUtil.close(out);
    }
    
    /**
     * 登录方法，未被调用，spring-security实际自己内部通过过滤器实现了登录
     * @param form 参数
     * @param request 参数
     * @param response 返回
     * @return 登录结果
     */
    @Operation(summary = "用户名密码登录", description = "用户名密码登录",
            parameters = {@Parameter(name = "form", description = "登录参数")})
    @ApiResponse(responseCode = "2xx",description = "用户名密码登录")
    @PostMapping("/login")
    public ReturnT<String> login(@RequestBody SysLoginForm form, HttpServletRequest request, HttpServletResponse response){
        try{
            boolean captcha = sysCaptchaService.validate(form.getUuid(), form.getCaptcha());
            if(!captcha){
                return ReturnT.error("验证码不正确");
            }
            if (StringUtils.isEmpty(form.getTerminal())) {
                return ReturnT.error("请传入终端类型");
            }
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(form.getUsername(), form.getPassword());
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            //认证未通过，给出提示
            if(Objects.isNull(authentication)){
                return ReturnT.error("登录失败");
            }
            SecurityUser user = (SecurityUser) authentication.getDetails();
            SysUser sysUser = sysUserService.getById(user.getId());
            sysUser.setId(user.getId());
            sysUser.setLoginTime(LocalDateTime.now());
            sysUser.setLoginTerminal(form.getTerminal());
            sysUserService.updateById(sysUser);
            Duration duration = Duration.between(sysUser.getLastChangedPwdTime(), LocalDateTime.now());
            long days = duration.toDays();
            if(passwordExpired.getDays() - days < passwordExpired.getAlarm()) {
                if(passwordExpired.isForceChangePwd() && passwordExpired.getDays() - days < passwordExpired.getForceDays()) {
                    return ReturnT.error(976, "您的密码即将过期，请您修改密码！");
                }
                return ReturnT.error(977, "您的密码即将过期，请您尽快修改密码。如到期未修改账户将被停用！");
            }
            return ReturnT.ok("登录成功");
        }catch(Exception e){
            log.error(e.getMessage(), e);
            return ReturnT.error("登录失败");
        }
    }
    
    /**
     * 登出方法，未被调用，spring-security实际自己内部通过过滤器实现了登录
     * @param request 参数
     * @param response 返回
     * @return 结果
     */
    @Operation(summary = "用户登出", description = "用户登出")
    @ApiResponse(responseCode = "2xx",description = "用户登出")
    @PostMapping("/logout")
    public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response){
        SecurityContextHolder.clearContext();
        return ReturnT.ok("注销成功");
    }
    
    /**
     * 加载当前登陆用户的菜单
     * @param terminal 终端 Constant.TerminalType
     * @return 当前用户的菜单
     */
    @Operation(summary = "加载当前登陆用户的菜单", description = "加载当前登陆用户的菜单",
            parameters = {@Parameter(name = "terminal", description = "当前登陆终端")})
    @ApiResponse(responseCode = "2xx",description = "加载当前登陆用户的菜单")
    @GetMapping("/getAllMenuForUser/{terminal}")
    public ReturnT<List<AdminRoute>> getAllMenuForUser(@PathVariable("terminal") String terminal){
        //当前登陆用户
        SecurityUser user = UserUtil.getCurrentUser();
        List<AdminRoute> routes = sysUserService.getAllMenuForUser(user, terminal);
        return ReturnT.ok(routes);
    }
    
    /**
     * 加载当前登陆用户的菜单
     * @param terminal 终端 Constant.TerminalType
     * @return 当前用户的菜单
     */
    @Operation(summary = "加载当前登陆用户的菜单", description = "加载当前登陆用户的菜单",
            parameters = {@Parameter(name = "terminal", description = "当前登陆终端")})
    @ApiResponse(responseCode = "2xx",description = "加载当前登陆用户的菜单")
    @GetMapping("/getAllMenuForUserVue/{terminal}")
    public ReturnT<List<AdminRouteVue>> getAllMenuForUserVue(@PathVariable("terminal") String terminal){
        //当前登陆用户
        SecurityUser user = UserUtil.getCurrentUser();
        List<AdminRouteVue> routes = sysUserService.getAllMenuForUserVue(user, terminal);
        return ReturnT.ok(routes);
    }
    
    /**
     * 设置角色资源权限
     * @param role 角色资源
     * @return 设置结果
     */
    @Operation(summary = "通过角色设置资源权限", description = "通过角色设置资源权限",
            parameters = {@Parameter(name = "role", description = "资源权限")})
    @ApiResponse(responseCode = "2xx",description = "用户登出")
    @PostMapping("/setResourceByRole")
    @PreAuthorize("hasAuthority('sys:role:setmenu')")
    public ReturnT<String> setResourceByRole(@RequestBody SysRole role){
        try{
            sysRoleMenuService.setResourceByRole(role);
            return ReturnT.ok("操作成功");
        }catch(Exception e){
            log.error(e.getMessage(), e);
            return ReturnT.error("通过角色设置资源权限失败");
        }
    }
    
    /**
     * 加载角色的菜单
     * @param roleId 角色id
     * @return 当前用户的菜单
     */
    @Operation(summary = "加载当前登陆用户的菜单", description = "加载当前登陆用户的菜单",
            parameters = {@Parameter(name = "terminal", description = "当前登陆终端")})
    @ApiResponse(responseCode = "2xx",description = "加载当前登陆用户的菜单")
    @GetMapping("/getMenuListByRoleId/{roleId}")
    public ReturnT<Map<String, Object>> getMenuListByRoleId(@PathVariable("roleId") String roleId){
        Map<String, Object> routes = sysRoleMenuService.getAllMenuForRole(roleId);
        return ReturnT.ok(routes);
    }
    
    /**
     * 加载当前登陆用户的信息
     * @return 当前用户的信息
     */
    @Operation(summary = "加载当前登陆用户的信息", description = "加载当前登陆用户的信息")
    @ApiResponse(responseCode = "2xx",description = "加载当前登陆用户的信息")
    @GetMapping("/getUserInfoSession")
    public ReturnT<SecurityUser> getUserInfoSession(){
        //当前登陆用户
        SecurityUser user = UserUtil.getCurrentUser();
        return ReturnT.ok(user);
    }

    /**
     * 加载当前登陆用户的首页看板，配置优先级规则：用户>岗位>角色>机构
     * @return 看板ID
     */
    @ApiResponse(responseCode = "2xx",description = "加载当前登陆用户的首页看板")
    @GetMapping("/getCurrentDashboardSetting")
    public ReturnT<String> getCurrentDashboardSetting(){
        return ReturnT.ok(sysDashboardSettingService.getDashboardSettingByUserId(UserUtil.getCurrentUser().getId()));
    }
}
